package com.gxc.string;

import java.lang.*;
import java.lang.Integer;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Stack;

/**
 * 32. 最长有效括号
 给你一个只包含 '(' 和 ')' 的字符串，找出最长有效（格式正确且连续）括号子串的长度。
 */
public class LongestValidParentheses {

    public static void main(String[] args) {
        System.out.println(handle("(()"));
        System.out.println(handle(")()())"));
        System.out.println(handle("()(()"));
        System.out.println(handle("()(())"));
    }

    //dp
    public static int handle(String s) {
        int[] res = new int[s.length()];
        int max = 0;

        Stack<java.lang.Integer> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (stack.isEmpty()) {
                stack.push(i);
                res[i] = 0;
            } else {
                if (c == ')') {
                    if (s.charAt(stack.peek()) == '(') {
                        //匹配（）  计算长度
                        Integer index = stack.pop();
                        res[i] = i - index + 1 + (index==0?0:res[index-1]);
                    } else {
                        stack.push(i);
                        res[i] = 0;
                    }
                } else {
                    stack.push(i);
                }
            }
        }
        for (int i = 0; i < res.length; i++) {
            max = Math.max(max, res[i]);
        }
        return max;
    }

    /**
     * 官方dp 不用stack
     */
    class Solution {
        public int longestValidParentheses(String s) {
            int maxans = 0;
            int[] dp = new int[s.length()];
            for (int i = 1; i < s.length(); i++) {
                if (s.charAt(i) == ')') {
                    if (s.charAt(i - 1) == '(') {
                        dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
                    } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
                        dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
                    }
                    maxans = Math.max(maxans, dp[i]);
                }
            }
            return maxans;
        }
    }

    /**
     * 单纯用栈
     * 对于遇到的每个 ‘(’ ，我们将它的下标放入栈中
     对于遇到的每个 ‘)’ ，我们先弹出栈顶元素表示匹配了当前右括号：
     如果栈为空，说明当前的右括号为没有被匹配的右括号，
     我们将其下标放入栈中来更新我们之前提到的「最后一个没有被匹配的右括号的下标」
     如果栈不为空，当前右括号的下标减去栈顶元素即为「以该右括号为结尾的最长有效括号的长度」
     */

    class Solution2 {
        public int longestValidParentheses(String s) {
            int maxans = 0;
            Deque<Integer> stack = new LinkedList<Integer>();
            stack.push(-1);
            for (int i = 0; i < s.length(); i++) {
                if (s.charAt(i) == '(') {
                    stack.push(i);
                } else {
                    stack.pop();
                    if (stack.isEmpty()) {
                        stack.push(i);
                    } else {
                        maxans = Math.max(maxans, i - stack.peek());
                    }
                }
            }
            return maxans;
        }
    }

    /**
     * 不需要额外的空间
     思路和算法

     在此方法中，我们利用两个计数器 left 和 right 。首先，我们从左到右遍历字符串，对于遇到的每个 ‘(’，我们增加 left 计数器，对于遇到的每个 ‘)’ ，我们增加 right 计数器。每当 left 计数器与 right 计数器相等时，我们计算当前有效字符串的长度，并且记录目前为止找到的最长子字符串。当 right 计数器比 left 计数器大时，我们将 left 和 right 计数器同时变回 0。

     这样的做法贪心地考虑了以当前字符下标结尾的有效括号长度，每次当右括号数量多于左括号数量的时候之前的字符我们都扔掉不再考虑，重新从下一个字符开始计算，但这样会漏掉一种情况，就是遍历的时候左括号的数量始终大于右括号的数量，即 (() ，这种时候最长有效括号是求不出来的。

     解决的方法也很简单，我们只需要从右往左遍历用类似的方法计算即可，只是这个时候判断条件反了过来：

     当 left 计数器比 right 计数器大时，我们将 left 和 right 计数器同时变回 0
     当 left 计数器与 right 计数器相等时，我们计算当前有效字符串的长度，并且记录目前为止找到的最长子字符串
     这样我们就能涵盖所有情况从而求解出答案。
     */
    class Solution3 {
        public int longestValidParentheses(String s) {
            int left = 0, right = 0, maxlength = 0;
            for (int i = 0; i < s.length(); i++) {
                if (s.charAt(i) == '(') {
                    left++;
                } else {
                    right++;
                }
                if (left == right) {
                    maxlength = Math.max(maxlength, 2 * right);
                } else if (right > left) {
                    left = right = 0;
                }
            }
            left = right = 0;
            for (int i = s.length() - 1; i >= 0; i--) {
                if (s.charAt(i) == '(') {
                    left++;
                } else {
                    right++;
                }
                if (left == right) {
                    maxlength = Math.max(maxlength, 2 * left);
                } else if (left > right) {
                    left = right = 0;
                }
            }
            return maxlength;
        }
    }


}
